using Microsoft.Extensions.Logging;
using SeataNet.Core.Configuration;
using SeataNet.Core.Exceptions;
using SeataNet.Core.RPC;
using System.Net.Http.Headers;
using System.Text;

namespace SeataNet.Core.RPC
{
    /// <summary>
    /// 默认RPC客户端实现
    ///
    /// 职责：
    /// 1. 建立与Seata服务器的HTTP通信连接
    /// 2. 序列化/反序列化RPC请求和响应
    /// 3. 处理超时、异常等错误情况
    /// 4. 支持同步和异步请求方式
    ///
    /// 工作流程：
    /// 1. 通过InitializeAsync()初始化客户端，配置服务器地址和应用信息
    /// 2. 通过SendAsync<TRequest, TResponse>()发送双向请求
    /// 3. 通过SendOnewayAsync()发送单向请求（不需要响应）
    /// 4. 通过DestroyAsync()释放资源
    /// </summary>
    public class DefaultRpcClient : IRpcClient
    {
        private readonly ILogger<DefaultRpcClient> _logger;
        private readonly HttpClient _httpClient;
        private bool _isInitialized = false;
        private string? _serverAddress;
        private string? _applicationId;
        private string? _transactionServiceGroup;

        /// <summary>
        /// 获取连接状态
        /// </summary>
        public bool IsConnected => _isInitialized;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="logger">日志记录器</param>
        /// <param name="httpClient">HTTP客户端</param>
        public DefaultRpcClient(ILogger<DefaultRpcClient> logger, HttpClient httpClient)
        {
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
            _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
        }

        /// <summary>
        /// 是否已初始化
        /// </summary>
        public bool IsInitialized => _isInitialized;

        /// <summary>
        /// 初始化客户端
        ///
        /// 处理流程：
        /// 1. 检查客户端是否已初始化，若已初始化则直接返回
        /// 2. 保存服务器地址、应用ID等配置信息
        /// 3. 设置HttpClient的基础地址
        /// 4. 添加必要的请求头（ApplicationId、TransactionServiceGroup）
        /// 5. 标记客户端为已初始化状态
        /// </summary>
        /// <param name="serverAddress">Seata服务器地址（如http://localhost:8091）</param>
        /// <param name="applicationId">当前应用的唯一标识</param>
        /// <param name="transactionServiceGroup">事务服务组名称，用于服务发现</param>
        /// <returns>初始化是否成功</returns>
        public async Task<bool> InitializeAsync(string serverAddress, string applicationId, string transactionServiceGroup)
        {
            if (_isInitialized)
            {
                _logger.LogWarning("RPC client is already initialized");
                return true;
            }

            try
            {
                _serverAddress = serverAddress;
                _applicationId = applicationId;
                _transactionServiceGroup = transactionServiceGroup;

                // 设置HttpClient基础地址
                _httpClient.BaseAddress = new Uri(serverAddress);

                // 设置默认请求头
                _httpClient.DefaultRequestHeaders.Add("ApplicationId", applicationId);
                _httpClient.DefaultRequestHeaders.Add("TransactionServiceGroup", transactionServiceGroup);

                _isInitialized = true;
                _logger.LogInformation("RPC client initialized with server address: {serverAddress}", serverAddress);

                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to initialize RPC client");
                return false;
            }
        }
        
        /// <summary>
        /// 发送RPC请求并获取响应（双向通信）
        ///
        /// 处理流程：
        /// 1. 检查客户端是否已初始化
        /// 2. 使用CancellationTokenSource设置请求超时时间
        /// 3. 将请求对象序列化为JSON格式
        /// 4. 通过HTTP POST请求发送到服务器
        /// 5. 检查HTTP响应状态码
        /// 6. 反序列化响应数据并返回
        ///
        /// 线程安全：HttpClient本身是线程安全的，可以并发调用
        ///
        /// 异常处理：
        /// - 超时异常：由CancellationToken自动抛出OperationCanceledException
        /// - 网络异常：HTTP异常由EnsureSuccessStatusCode()抛出
        /// - 反序列化异常：由JsonSerializer.Deserialize()抛出
        /// 所有异常均被捕获并包装为RpcException
        /// </summary>
        /// <typeparam name="TRequest">请求对象的类型</typeparam>
        /// <typeparam name="TResponse">响应对象的类型</typeparam>
        /// <param name="request">待发送的请求对象</param>
        /// <param name="timeout">请求超时时间，单位为毫秒，默认30秒</param>
        /// <returns>服务器返回的响应对象</returns>
        /// <exception cref="RpcException">RPC通信失败或数据反序列化失败</exception>
        public async Task<TResponse> SendAsync<TRequest, TResponse>(TRequest request, int timeout = 30000) where TRequest : class where TResponse : class
        {
            EnsureInitialized();

            try
            {
                // 设置超时：创建自动取消令牌源，在指定毫秒数后自动触发取消
                using var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(timeout));

                // 序列化请求：将请求对象转换为JSON字符串
                var json = System.Text.Json.JsonSerializer.Serialize(request);
                var content = new StringContent(json, Encoding.UTF8, MediaTypeHeaderValue.Parse("application/json"));

                // 发送请求：通过HTTP POST方法将请求发送到服务器
                var response = await _httpClient.PostAsync("/api/seata", content, cts.Token);

                // 确保响应成功：检查HTTP状态码，非2xx状态会抛出异常
                response.EnsureSuccessStatusCode();

                // 反序列化响应：从响应体读取JSON数据并转换为指定类型
                var responseJson = await response.Content.ReadAsStringAsync();
                var result = System.Text.Json.JsonSerializer.Deserialize<TResponse>(responseJson);

                if (result == null)
                {
                    throw new RpcException("Failed to deserialize response");
                }

                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to send request");
                throw new RpcException("Failed to send request", ex);
            }
        }

        /// <summary>
        /// 发送请求并获取响应（支持取消令牌）
        ///
        /// 这是SendAsync<TRequest, TResponse>(TRequest, int)的重载版本，
        /// 允许调用者传入自己的CancellationToken以支持更灵活的异步取消控制
        ///
        /// 适用场景：
        /// - 调用者需要在超时之前主动取消请求
        /// - 处理多个RPC请求的聚合调用，需要统一的超时管理
        /// - 与其他异步操作共享同一个取消令牌
        /// </summary>
        /// <typeparam name="TRequest">请求对象的类型</typeparam>
        /// <typeparam name="TResponse">响应对象的类型</typeparam>
        /// <param name="request">待发送的请求对象</param>
        /// <param name="cancellationToken">用于取消请求的令牌</param>
        /// <returns>服务器返回的响应对象</returns>
        /// <exception cref="RpcException">RPC通信失败或数据反序列化失败</exception>
        /// <exception cref="OperationCanceledException">请求被取消</exception>
        public async Task<TResponse> SendAsync<TRequest, TResponse>(TRequest request, CancellationToken cancellationToken) where TRequest : class where TResponse : class
        {
            EnsureInitialized();

            try
            {
                // 序列化请求：将请求对象转换为JSON字符串
                var json = System.Text.Json.JsonSerializer.Serialize(request);
                var content = new StringContent(json, Encoding.UTF8, MediaTypeHeaderValue.Parse("application/json"));

                // 发送请求：传入外部取消令牌，允许调用者控制超时
                var response = await _httpClient.PostAsync("/api/seata", content, cancellationToken);

                // 确保响应成功：检查HTTP状态码
                response.EnsureSuccessStatusCode();

                // 反序列化响应：从响应体读取JSON数据
                var responseJson = await response.Content.ReadAsStringAsync();
                var result = System.Text.Json.JsonSerializer.Deserialize<TResponse>(responseJson);

                if (result == null)
                {
                    throw new RpcException("Failed to deserialize response");
                }

                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to send request");
                throw new RpcException("Failed to send request", ex);
            }
        }
        
        /// <summary>
        /// 发送单向请求（不需要响应）
        ///
        /// 适用场景：
        /// - 发送事件通知、日志记录等不需要响应的消息
        /// - 性能要求高，不需要等待服务器响应
        /// - 即使请求失败也不影响业务流程
        ///
        /// 处理流程：
        /// 1. 检查客户端是否已初始化
        /// 2. 序列化请求对象为JSON
        /// 3. 通过HTTP POST发送请求
        /// 4. 检查HTTP响应状态，但不需要读取响应体
        /// 5. 异常时仅记录日志，不抛出异常
        ///
        /// 注意：单向请求发送失败时不会抛出异常，仅返回false
        /// </summary>
        /// <typeparam name="TRequest">请求对象的类型</typeparam>
        /// <param name="request">待发送的请求对象</param>
        /// <returns>发送是否成功（true表示HTTP响应成功，false表示失败）</returns>
        public async Task<bool> SendOnewayAsync<TRequest>(TRequest request) where TRequest : class
        {
            EnsureInitialized();

            try
            {
                // 序列化请求：将请求对象转换为JSON字符串
                var json = System.Text.Json.JsonSerializer.Serialize(request);
                var content = new StringContent(json, Encoding.UTF8, MediaTypeHeaderValue.Parse("application/json"));

                // 发送请求：单向消息不需要等待响应
                var response = await _httpClient.PostAsync("/api/seata/oneway", content);

                // 确保响应成功：检查HTTP状态码
                response.EnsureSuccessStatusCode();

                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to send oneway request");
                return false;
            }
        }

        /// <summary>
        /// 发送消息到指定地址
        ///
        /// 这个方法用于在客户端之间直接通信，不依赖预设的服务器地址。
        /// 每次调用都会创建临时的HttpClient，用于向特定的远程地址发送消息。
        ///
        /// 使用场景：
        /// - 发送消息到另一个应用/服务的特定地址
        /// - 应用间直接通信，而不通过中央协调器
        /// - 推送通知、异步消息等
        ///
        /// 处理流程：
        /// 1. 序列化消息对象为JSON
        /// 2. 创建临时HttpClient并设置目标地址
        /// 3. 通过HTTP POST发送消息
        /// 4. 检查响应状态
        /// </summary>
        /// <typeparam name="TMessage">消息对象的类型</typeparam>
        /// <param name="address">目标服务的HTTP地址（如http://server:port）</param>
        /// <param name="message">待发送的消息对象</param>
        /// <exception cref="RpcException">消息发送失败</exception>
        public async Task SendMessageAsync<TMessage>(string address, TMessage message) where TMessage : class
        {
            try
            {
                // 序列化消息：将消息对象转换为JSON字符串
                var json = System.Text.Json.JsonSerializer.Serialize(message);
                var content = new StringContent(json, Encoding.UTF8, "application/json");

                // 创建临时HttpClient用于特定地址
                // 注意：生产环境中应该考虑连接复用，避免频繁创建HttpClient
                using var client = new HttpClient();
                client.BaseAddress = new Uri(address);

                // 发送请求：单向消息
                var response = await client.PostAsync("/api/seata/message", content);
                response.EnsureSuccessStatusCode();

                _logger.LogInformation("Message sent to {address}", address);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to send message to {address}", address);
                throw new RpcException($"Failed to send message to {address}", ex);
            }
        }

        /// <summary>
        /// 发送消息并获取响应
        ///
        /// 与SendMessageAsync<TMessage>(string, TMessage)不同，此方法期望获得响应。
        /// 用于应用间的双向通信。
        ///
        /// 使用场景：
        /// - 向另一个应用发送请求并等待响应
        /// - 应用间的RPC调用
        /// - 需要等待处理结果的异步请求
        ///
        /// 处理流程：
        /// 1. 序列化请求对象为JSON
        /// 2. 创建临时HttpClient并设置目标地址
        /// 3. 通过HTTP POST发送请求
        /// 4. 读取并反序列化响应数据
        /// 5. 返回响应对象
        /// </summary>
        /// <typeparam name="TResponse">响应对象的类型</typeparam>
        /// <param name="address">目标服务的HTTP地址（如http://server:port）</param>
        /// <param name="request">待发送的请求对象</param>
        /// <returns>服务器返回的响应对象</returns>
        /// <exception cref="RpcException">消息发送失败或响应反序列化失败</exception>
        public async Task<TResponse> SendMessageAsync<TResponse>(string address, object request) where TResponse : class
        {
            try
            {
                // 序列化请求：将请求对象转换为JSON字符串
                var json = System.Text.Json.JsonSerializer.Serialize(request);
                var content = new StringContent(json, Encoding.UTF8, MediaTypeHeaderValue.Parse("application/json"));

                // 创建临时HttpClient用于特定地址
                using var client = new HttpClient();
                client.BaseAddress = new Uri(address);

                // 发送请求：双向消息
                var response = await client.PostAsync("/api/seata/message", content);
                response.EnsureSuccessStatusCode();

                // 反序列化响应：从响应体读取JSON数据
                var responseJson = await response.Content.ReadAsStringAsync();
                var result = System.Text.Json.JsonSerializer.Deserialize<TResponse>(responseJson);

                if (result == null)
                {
                    throw new RpcException("Failed to deserialize response");
                }

                _logger.LogInformation("Message sent to {address} and received response", address);
                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to send message to {address}", address);
                throw new RpcException($"Failed to send message to {address}", ex);
            }
        }

        /// <summary>
        /// 销毁客户端并释放资源
        ///
        /// 此方法应在应用关闭或不再需要使用RPC客户端时调用。
        ///
        /// 清理内容：
        /// 1. 释放HttpClient占用的网络资源
        /// 2. 关闭与服务器的连接
        /// 3. 清除内存中的配置信息
        /// 4. 标记客户端为未初始化状态
        ///
        /// 重复调用是安全的，第二次及之后的调用会直接返回
        /// </summary>
        /// <returns>销毁是否成功</returns>
        public async Task<bool> DestroyAsync()
        {
            if (!_isInitialized)
            {
                return true;
            }

            try
            {
                // 释放HttpClient资源
                _httpClient?.Dispose();

                _isInitialized = false;
                _logger.LogInformation("RPC client destroyed");

                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to destroy RPC client");
                return false;
            }
        }

        /// <summary>
        /// 私有方法：确保客户端已初始化
        ///
        /// 用于验证客户端状态。如果未初始化，抛出InvalidOperationException异常。
        /// 这个方法应该在所有需要网络通信的公开方法中被调用，以确保前置条件满足。
        /// </summary>
        /// <exception cref="InvalidOperationException">客户端未初始化</exception>
        private void EnsureInitialized()
        {
            if (!_isInitialized)
            {
                throw new InvalidOperationException("RPC client is not initialized");
            }
        }
    }
}